﻿// --------------------------------------------------------------------------------------------------------------------- 
// <copyright file="Selector.cs" company="Microsoft.Blueprints.Contrib Team">
//   (C) 2009 Microsoft.Blueprints.Contrib Team
// </copyright>
// <summary>
//   Defines the BuildSystemFinder type.
// </summary>
// ---------------------------------------------------------------------------------------------------------------------
namespace Microsoft.Blueprints.Contrib.Dialogs
{
    #region Using Directives

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Windows.Forms;

    using Contracts.T4;

    using Microsoft.Blueprints.Contrib.T4;
    using Microsoft.SoftwareFactories.Blueprints;

    #endregion

    /// <summary>
    /// Defines the BuildSystemFinder type.
    /// </summary>
    public partial class Selector : Form, IT4TemplateProperties
    {
        /// <summary>
        /// Dictionary which contains the properties and data exposed by the T4 Template
        /// </summary>
        private Dictionary<string, T4PropertyData> properties = new Dictionary<string, T4PropertyData>();

        /// <summary>
        /// Initializes a new instance of the <see cref="Selector"/> class.
        /// </summary>
        public Selector()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Selector"/> class.
        /// </summary>
        /// <param name="template">
        /// The template.
        /// </param>
        public Selector(T4Template template) : this()
        {
            this.Template = template;
            this.Properties = template.Properties;
        }

        /// <summary>
        /// Gets or sets Filter.
        /// </summary>
        /// <value>
        /// The filter.
        /// </value>
        public string Filter
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets InitialDirectory.
        /// </summary>
        /// <value>
        /// The initial directory.
        /// </value>
        public string InitialDirectory
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets PathMode.
        /// </summary>
        /// <value>
        /// The path mode.
        /// </value>
        public SelectorPathMode PathMode
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the PathSuffix.
        /// </summary>
        /// <remarks>
        /// This property is used to append an additional 
        /// directory path onto the selected filepath.
        /// </remarks>
        /// <value>
        /// The directory suffix.
        /// </value>
        public string PathSuffix
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the Properties.
        /// </summary>
        /// <value>
        /// The properties.
        /// </value>
        public Dictionary<string, T4PropertyData> Properties
        {
            get
            {
                return this.properties;
            }

            set
            {
                this.properties = value;
            }
        }

        /// <summary>
        /// Gets or sets SelectorMode.
        /// </summary>
        /// <value>
        /// The selector mode.
        /// </value>
        public SelectorMode SelectorMode
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets Template.
        /// </summary>
        /// <value>
        /// The template.
        /// </value>
        public T4Template Template
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets Filter.
        /// </summary>
        /// <value>
        /// The filter.
        /// </value>
        public string Title
        {
            get
            {
                return this.Text;
            }

            set
            {
                this.Text = value;
            }
        }

        /// <summary>
        /// Cancel out of the form, invoking no actions.
        /// </summary>
        /// <param name="sender">
        /// The sender of the event.
        /// </param>
        /// <param name="e">
        /// The event data.
        /// </param>
        private void OnCancelButtonClick(object sender, EventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Invokes the Open File Dialog.
        /// </summary>
        /// <param name="sender">
        /// The sender of the event.
        /// </param>
        /// <param name="e">
        /// The event data.
        /// </param>
        private void OnInvokeOpenFileDialogButton(object sender, EventArgs e)
        {
            this.openFileDialog.InitialDirectory = this.InitialDirectory;
            this.openFileDialog.Filter = this.Filter;

            if (this.openFileDialog.ShowDialog() == DialogResult.OK)
            {
                FileInfo selectedFile = new FileInfo(this.openFileDialog.FileName);

                if (this.SelectorMode == SelectorMode.Directory)
                {
                    DirectoryInfo selectedDirectory = selectedFile.Directory;

                    if (selectedDirectory != null)
                    {
                        this.directoryPathTextbox.Text = selectedDirectory.FullName;
                    }
                }
                else
                {
                    this.directoryPathTextbox.Text = selectedFile.FullName;
                }
            }
        }

        /// <summary>
        /// Generate the Relative Path from the selected paths and exit the form.
        /// </summary>
        /// <param name="sender">
        /// The sender of the event.
        /// </param>
        /// <param name="e">
        /// The event data.
        /// </param>
        private void OnOkButtonClick(object sender, EventArgs e)
        {
            string path = string.Empty;

            path = this.PathMode == SelectorPathMode.Relative ? 
                this.RelativePathTo(this.InitialDirectory, this.directoryPathTextbox.Text) : this.directoryPathTextbox.Text;

            this.Properties[this.Properties.Keys.FirstOrDefault()] = new T4PropertyData(path, typeof(string));

            this.Close();
        }

        /// <summary>
        /// Generates a relative path based on the difference between the
        /// fromPath and the toPath.
        /// </summary>
        /// <remarks>
        /// This is used to calculate the DeploymentFactory path within the
        /// Build System compared to the current installer projects.
        /// </remarks>
        /// <param name="fromPath">
        /// The from path.
        /// </param>
        /// <param name="toPath">
        /// The to path.
        /// </param>
        /// <returns>
        /// A relative path - i.e. "../../../Build/"
        /// </returns>
        private string RelativePathTo(string fromPath, string toPath)
        {
            // If a directory suffix has been specified, append it to the toPath.
            if (!string.IsNullOrEmpty(this.PathSuffix))
            {
                toPath = Path.Combine(toPath, this.PathSuffix);
            }

            // we need to ensure that path ends with a / otherwise path calculation is off by one directory.
            Func<string, string> ensureFullPath = path =>
                                                  {
                                                      if (path[path.Length - 1] != Path.DirectorySeparatorChar)
                                                      {
                                                          path += Path.DirectorySeparatorChar;
                                                      }

                                                      return path;
                                                  };

            Uri fromPathUri = new Uri(ensureFullPath(fromPath));
            Uri toPathUri = new Uri(ensureFullPath(toPath));
            Uri relativeUri = fromPathUri.MakeRelativeUri(toPathUri);

            string relativePath = relativeUri.ToString();

            if (relativePath.EndsWith("/"))
            {
                relativePath = relativePath.TrimEnd('/');
            }

            // Swap the forward slash for a backslash
            relativePath = relativePath.Replace('/', '\\');

            return relativePath;
        }
    }
}